# player.py - Jukee
#
# Copyright (C) 2008 Ruben Medellin <ruben.medellin.c@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the
#    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
#    Boston, MA  02111-1307  USA
 
"""
This module contains the classes needed to play the audio stream on the server side
"""

import gobject, pygst
pygst.require("0.10")
import gst
import gtk
from threading import Thread
from jukee.utils.utils import *
from jukee.libs.Observable import Observable

#################### Player ####################

class Player(Thread, Observable):
  """
  Class that contains the methods for audio playing.
  """

  NEW_SONG_PLAYING    = 'new_song_playing'

  def __init__(self, host, port,
        volume = 1.0,
    ):
    """
    Creates the pipeline at the server side to play audio
    The gst chain is [tcpserversrc ! decodebin ! audioconvert ! alsasink]
    """

    Thread.__init__(self)
    Observable.__init__(self)
    self.setDaemon(True)

    debug("Creating Player instance (host=" + host + ", port=" + str(port) + ")")

    ## TODO: Configure audio output (scan for devices)

    # Create the pipeline
    self.pipeline = gst.Pipeline("server")
    #self.pipeline.set_clock(gst.system_clock_obtain())

    # Create the elements in the pipeline
    self.tcpsrc = gst.element_factory_make("tcpserversrc", "__source__")
    self.decode = gst.element_factory_make("decodebin", "__decode__")
    self.convert = gst.element_factory_make("audioconvert", "__convert__")
    self.volume = gst.element_factory_make("volume", "__volume__")
    self.sink = gst.element_factory_make("alsasink", "__sink__")
    
    # Set elements' properties 
    self.pipeline.set_property("auto-flush-bus", False)    # Needed?
    self.tcpsrc.set_property("host", host)
    self.tcpsrc.set_property("port", port)
    self.tcpsrc.set_property("typefind", True)
    self.volume.set_property("volume", volume)             ## TODO: Configurable

    # Add elements to the pipeline
    self.pipeline.add(self.tcpsrc)
    self.pipeline.add(self.decode)
    self.pipeline.add(self.convert)
    self.pipeline.add(self.volume)
    self.pipeline.add(self.sink)

    # Add callback for new decoded pads
    def new_decode_pad(dbin, pad, islast):
      pad.link(self.convert.get_pad("sink"))
      debug("Pad linked")
      self.broadcastEvent(Player.NEW_SONG_PLAYING, self.get_song_info())
      
    self.decode.connect("new-decoded-pad", new_decode_pad)

    # Link elements altogether
    self.tcpsrc.link(self.decode)
    self.convert.link(self.volume)
    self.volume.link(self.sink)

    # Add callback to pipeline bus for events
    def watch_bus_event(bus, message, pipeline = self.pipeline):
      
      t = message.type
      if t == gst.MESSAGE_EOS:
        debug("EOS received")
      elif t == gst.MESSAGE_ERROR:
        err, deb = message.parse_error()
        debug("GST ERROR: " + str(err) + " - " + str(deb))
      elif t == gst.MESSAGE_STATE_CHANGED:
        previous, current, pending = message.parse_state_changed()
        if current == gst.STATE_PLAYING and message.src == self.pipeline:
          debug("Server pipeline playing")
      return True

    bus = self.pipeline.get_bus()
    bus.add_watch(watch_bus_event, self.pipeline)
    
    # The server is ready to play
    debug("Server ready")
        
    # Create the main loop object
    self.loop = gobject.MainLoop()
    debug("Main loop created")

 
  def play(self):
    """
    Sets the pipeline into playing state
    """

    ## FIXME: === IMPORTANT! === By now, calling gtk.gdk.threads_init each time the pipeline
    ## is set to playing state works, even when another gtk program in other thread calls
    ## this function. Don't know the consequences or repeteadily call this function, just works
    ## by now
    gtk.gdk.threads_init()
    gtk.gdk.threads_enter()
    if not self.pipeline.set_state(gst.STATE_PLAYING):
      error("Error playing pipeline")
      gtk.gdk.threads_leave()
    else:
      debug("Pipeline set to state gst.STATE_PLAYING")
      gtk.gdk.threads_leave()
      try:
        self.loop.run()
      except:
        error("Error during main loop")
        self.stop()
        raise

  def pause(self):
    debug("Pausing pipeline")
    self.pipeline.set_state(gst.STATE_PAUSED)

  def resume(self):
    debug("Resuming pipeline")
    self.pipeline.set_state(gst.STATE_PLAYING)

  def run(self):
    self.play()

  def refresh(self):
    """Stops the pipeline to be able to receive streaming again"""

    debug("Refreshing pipeline")
    self.stop()
    self.play()
    debug("Pipeline refreshed")

  def stop(self):
    """Stops the pipeline and cleans up elements"""

    self.pipeline.set_state(gst.STATE_NULL)
    if self.loop:
      self.loop.quit()
    #self.pipeline = self.tcpsrc = self.decode = self.convert = self.sink = self.loop = None
    debug("Player stopped")

  def get_song_info(self):
    """Gets song information"""
    warn("Player#get_song_info: NOT IMPLEMENTED")
    return str(self.pipeline)
